package robot.arm.dao.exql.impl;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import robot.arm.dao.exql.ExqlContext;
import robot.arm.dao.exql.ExqlPattern;
import robot.arm.dao.exql.ExqlUnit;
import robot.arm.dao.exql.impl.unit.BunchUnit;
import robot.arm.dao.exql.impl.unit.ChoiceUnit;
import robot.arm.dao.exql.impl.unit.EmptyUnit;
import robot.arm.dao.exql.impl.unit.ExprUnit;
import robot.arm.dao.exql.impl.unit.ForEachUnit;
import robot.arm.dao.exql.impl.unit.JoinExprUnit;
import robot.arm.dao.exql.impl.unit.OptionUnit;
import robot.arm.dao.exql.impl.unit.TextUnit;

/**
 * 实现语句编译器。
 * 
 */
public class ExqlCompiler {

	// 编译的常量
	private static final char BRACE_LEFT = '(';

	private static final char BRACE_RIGHT = ')';

	private static final char BLOCK_LEFT = '{';

	private static final char BLOCK_RIGHT = '}';

	private static final String SHARP = "#";

	private static final String JOIN = "!";

	private static final String KEYWORD_IF = "if";

	private static final String KEYWORD_FOR = "for";

	private static final String SHARP_ELSE = "#else";

	// 正则表达式
	private static final Pattern PATTERN_KEYWORD = Pattern.compile( // NL
			"\\:\\:|([\\:\\$]{1}[a-zA-Z0-9_\\.]+)|\\{([^\\{\\}]+)\\}\\?|#(#|!|if|for)?");
	
	// in表达式
	private static final Pattern PATTERN_IN = Pattern.compile(// NL
			"([a-zA-Z0-9_]*)\\s+in\\s+(.+)");

	// 待编译的语句
	private final String pattern;

	private final int length;

	// 编译位置
	private int position = 0;

	/**
	 * 创建语句编译器。
	 * 
	 * @param pattern
	 *            - 待编译的语句
	 */
	public ExqlCompiler(String pattern) {
		this.pattern = pattern;
		this.length = pattern.length();
	}

	/**
	 * 执行编译动作。
	 * 
	 * @return ExqlPattern 对象
	 */
	public ExqlPattern compile() {

		// 从语句编译出: ExqlPattern 对象
		return new ExqlPatternImpl(pattern, compileUnit());
	}

	/**
	 * 从语句编译出: ExqlUnit 对象。
	 * 
	 * 
	 * @return ExqlUnit 对象
	 */
	protected ExqlUnit compileUnit() {

		// 在输入中查找 PREFIX 字符
		Matcher matcher = PATTERN_KEYWORD.matcher(pattern);

		// 输出的单元列表
		ArrayList<ExqlUnit> units = new ArrayList<ExqlUnit>();

		// 组装位置
		int fromIndex = 0;

		while ((position < length) && matcher.find(position)) {

			// 修改当前位置
			position = matcher.end();

			// 检查 :expr | $expr 形式的子句
			String expr = matcher.group(1);
			if (expr != null) {

				// 创建文本子句
				if (matcher.start() > fromIndex) {
					units.add(new TextUnit(pattern.substring(fromIndex, matcher.start())));
				}

				// 创建 :expr | $expr 形式的子句
				if (expr.charAt(0) == '$') {

					// 创建 $expr 形式的子句, 作为拼接处理
					units.add(new JoinExprUnit(expr));
				} else {

					// 创建 :expr 形式的子句
					units.add(new ExprUnit(expr));
				}

				fromIndex = position;

				continue;
			}

			// 检查 {...}? 的语法
			String group = matcher.group(2);
			if (group != null) {

				// 创建文本子句
				if (matcher.start() > fromIndex) {
					units.add(new TextUnit(pattern.substring(fromIndex, matcher.start())));
				}

				// 编译 {...} 内部的子句
				ExqlCompiler compiler = new ExqlCompiler(group);
				ExqlUnit unit = compiler.compileUnit();

				// 创建 {...}? 形式的子句
				units.add(new OptionUnit(unit));

				fromIndex = position;

				continue;
			}

			// 检查 # 后面的关键字
			String keyword = matcher.group(3);

			// 处理 #(:expr) 形式的子句
			if (keyword == null) {

				// 获取括号内的内容
				expr = findBrace(BRACE_LEFT, BRACE_RIGHT);
				if (expr != null) {

					// 创建文本子句
					if (matcher.start() > fromIndex) {
						units.add(new TextUnit(pattern.substring(fromIndex, matcher.start())));
					}

					// 创建 #(:expr) 形式的表达式
					units.add(new ExprUnit(expr));

					fromIndex = position;
				}
			}
			// 处理 ##(:expr) 形式的子句
			else if (keyword.equals(SHARP) || keyword.equals(JOIN)) {

				// 获取括号内的内容
				expr = findBrace(BRACE_LEFT, BRACE_RIGHT);
				if (expr != null) {

					// 创建文本子句
					if (matcher.start() > fromIndex) {
						units.add(new TextUnit(pattern.substring(fromIndex, matcher.start())));
					}

					// 创建 ##(:expr) 形式的表达式
					units.add(new JoinExprUnit(expr));

					fromIndex = position;
				}
			}
			// 处理 #if(:expr) {...} #else {...} 形式的子句
			else if (keyword.equals(KEYWORD_IF)) {

				// 获取括号内的内容
				expr = findBrace(BRACE_LEFT, BRACE_RIGHT);
				if (expr != null) {

					// 编译 {...} 单元
					ExqlUnit unitIfTrue = compileBlock();
					if (unitIfTrue != null) {

						// 创建文本子句
						if (matcher.start() > fromIndex) {
							units.add(new TextUnit(pattern.substring(fromIndex, matcher.start())));
						}

						ExqlUnit unitIfFalse = null;

						// 匹配 #else {...} 子句
						if (match(SHARP_ELSE, position)) {

							// 编译 {...} 单元
							unitIfFalse = compileBlock();
						}

						// 创建 #if(:expr) {...} #else {...} 形式的子句
						units.add(new ChoiceUnit(expr, unitIfTrue, unitIfFalse));

						fromIndex = position;
					}
				}
			}
			// 处理 #for(variant in :expr) {...} 形式的子句
			else if (keyword.equals(KEYWORD_FOR)) {

				// 获取括号内的内容
				expr = findBrace(BRACE_LEFT, BRACE_RIGHT);
				if (expr != null) {

					// 编译 {...} 单元
					ExqlUnit unit = compileBlock();
					if (unit != null) {

						// 创建文本子句
						if (matcher.start() > fromIndex) {
							units.add(new TextUnit(pattern.substring(fromIndex, matcher.start())));
						}

						// 循环变量名
						String variant = null;

						// 解析 variant in :expr 表达式
						Matcher matcherIn = PATTERN_IN.matcher(expr);
						if (matcherIn.matches()) {
							variant = matcherIn.group(1);
							expr = matcherIn.group(2);
						}

						// 创建 #for(variant in :expr) {...} 形式的子句
						units.add(new ForEachUnit(expr, variant, unit));

						fromIndex = position;
					}
				}
			}
		}

		if (fromIndex < length) {
			// 写入 PREFIX 字符后的内容。
			units.add(new TextUnit(pattern.substring(fromIndex)));
		}

		if (units.size() > 1) {

			// 返回集合对象
			return new BunchUnit(units);

		} else if (!units.isEmpty()) {

			// 返回单个对象
			return units.get(0);

		} else {

			// 返回空对象
			return new EmptyUnit();
		}
	}

	/**
	 * 查找匹配的左括号, 忽略之前的空白字符。
	 * 
	 * 如果未找到匹配的左括号，函数返回 -1.
	 * 
	 * @param chLeft
	 *            - 匹配的左括号
	 * @param fromIndex
	 *            - 查找的起始位置
	 * 
	 * @return 左括号的位置, 如果未找到匹配的左括号，函数返回 -1.
	 */
	private int findLeftBrace(char chLeft, int fromIndex) {

		// 查找出现的左括号。
		for (int index = fromIndex; index < length; index++) {

			char ch = pattern.charAt(index);

			// 如果出现左括号，返回。
			if (ch == chLeft) {
				return index;
			}
			// 如果出现非空白字符，返回 - 1.
			else if (!Character.isWhitespace(ch)) {
				return -1;
			}
		}

		// 没有找到匹配的括号。
		return -1;
	}

	/**
	 * 查找匹配的右括号, 可以用于匹配 '{}', '[]', '()' 括号对。
	 * 
	 * 如果未找到匹配的右括号，函数返回 -1.
	 * 
	 * @param string
	 *            - 查找的字符串
	 * @param chLeft
	 *            - 匹配的左括号
	 * @param chRight
	 *            - 匹配的右括号
	 * @param fromIndex
	 *            - 查找的起始位置
	 * 
	 * @return 右括号的位置, 如果未找到匹配的右括号，函数返回 -1.
	 */
	private int findRightBrace(char chLeft, char chRight, int fromIndex) {

		int level = 0; // 记录括号重叠级别。

		// 查找匹配的右括号。
		for (int index = fromIndex; index < length; index++) {

			char ch = pattern.charAt(index);

			// 如果出现左括号，重叠级别增加。
			if (ch == chLeft) {
				level++;
			}
			// 如果出现右括号，重叠级别降低。
			else if (ch == chRight) {

				if (level == 0) {
					// 找到右括号。
					return index;
				}

				level--;
			}
		}

		// 没有找到匹配的括号。
		return -1;
	}

	/**
	 * 从当前位置查找匹配的一对括号, 并返回内容。
	 * 
	 * 如果有匹配的括号, 返回后的当前位置指向匹配的右括号后一个字符。
	 * 
	 * @param chLeft
	 *            - 匹配的左括号
	 * @param chRight
	 *            - 匹配的右括号
	 * 
	 * @return 返回括号内容, 如果没有括号匹配, 返回 <code>null</code>.
	 */
	private String findBrace(char chLeft, char chRight) {

		// 从当前位置查找查找匹配的 (...)
		int left = findLeftBrace(chLeft, position);
		if (left >= position) {

			int start = left + 1;
			int end = findRightBrace(chLeft, chRight, start);
			if (end >= start) {

				// 当前位置指向匹配的右括号后一个字符
				position = end + 1;

				// 返回匹配的括号内容
				return pattern.substring(start, end);
			}
		}

		return null; // 没有 (...) 匹配
	}

	/**
	 * 从当前位置查找匹配的: {...} 并编译出: ExqlUnit 对象。
	 * 
	 * 如果有匹配的: {...}, 编译后, 当前位置指向匹配的右括号后一个字符。
	 * 
	 * @return ExqlUnit, 如果没有: {...} 匹配, 返回 <code>null</code>.
	 */
	private ExqlUnit compileBlock() {

		// 查找匹配的 {...}
		String group = findBrace(BLOCK_LEFT, BLOCK_RIGHT);
		if (group != null) {

			// 编译 {...} 内部的子句
			ExqlCompiler compiler = new ExqlCompiler(group);
			return compiler.compileUnit();
		}

		return null; // 没有 {...} 匹配
	}

	/**
	 * 匹配指定的关键字。
	 * 
	 * 如果匹配成功, 当前位置是关键字最后一个字符之后。
	 * 
	 * @param keyword
	 *            - 匹配的关键字
	 * @param fromIndex
	 *            - 查找的起始位置
	 * 
	 * @return true / false
	 */
	private boolean match(String keyword, int fromIndex) {

		int match = 0;

		// 查找非空白字符。
		for (int index = fromIndex; index < length; index++) {

			char ch = pattern.charAt(index);

			if (!Character.isWhitespace(ch)) {
				match = index;
				break;
			}
		}

		// 匹配关键字内容。
		for (int index = 0; index < keyword.length(); index++) {

			char ch = pattern.charAt(match);

			if (ch != keyword.charAt(index)) {
				// 与关键字不匹配
				return false;
			}

			match++;
		}

		// 修改当前位置
		position = match;

		return true;
	}

	// 进行简单测试
	public static void main(String... args) throws Exception {

		String string = "SELECT :expr1, #($expr2.class)," + " WHERE #if(:expr3) {e = $expr3} #else {e IS NULL}"
				+ "#for(variant in $expr4.bytes) { AND c = :variant}" // NL
				+ " {AND d = :expr5}? {AND f = $expr6}?" // NL
				+ " BY #!(:expr7) ASC";

		// 在输入中查找 PREFIX 字符
		Matcher matcher = PATTERN_KEYWORD.matcher(string);

		int position = 0;

		while (matcher.find(position)) {

			System.out.println("===============================");
			System.out.println("group 0: " + matcher.group(0));
			System.out.println("group 1: " + matcher.group(1));
			System.out.println("group 2: " + matcher.group(2));
			System.out.println("group 3: " + matcher.group(3));

			position = matcher.end();
		}

		// 在循环表达式中查找
		matcher = PATTERN_IN.matcher("variant in :expr5");

		if (matcher.matches()) {

			System.out.println("===============================");
			System.out.println("group 0: " + matcher.group(0));
			System.out.println("group 1: " + matcher.group(1));
			System.out.println("group 2: " + matcher.group(2));
		}

		// 编译下列语句
		ExqlPattern pattern = new ExqlCompiler(string).compile();

		ExqlContext context = new ExqlContextImpl();

		HashMap<String, Object> map = new HashMap<String, Object>();

		map.put("expr1", "expr1_v");
		map.put("expr2", "expr2_v");
		map.put("expr3", "expr3_v");
		map.put("expr4", "expr4_v");
		map.put("expr5", "expr5_v");
		map.put("expr6", "expr6_v");
		map.put("expr7", "expr7_v");

		System.out.println(pattern.execute(context, map, map));
		System.out.println(Arrays.toString(context.getParams()));
	}
}
